{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tutorial 6: Function Secret Sharing\n",
    "In our library, we also support Function Secret Sharing, which refers to Boyle et al. 's paper: Function Secret Sharing: Improvements and Extensions.2016; Function Secret Sharing for Mixed-Mode and Fixed-Point Secure Computation.2021\n",
    "Function secret sharing is mainly the secret sharing of three functions, distributed point function (DPF), distributed comparison function (DCF) and distributed interval containment function (DICF).\n",
    "In addition, we also refer to paper: ``Grotto: Screaming fast (2+ 1)-PC for ℤ2n via (2, 2)-DPFs`` and paper: ``SIGMA: Secure GPT Inference with Function Secret Sharing`` to implement the functionality of DICF by DPF"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DPF\n",
    "Definition：\n",
    "$$\n",
    "f_{\\alpha,\\beta}(x)=\\begin{cases}\n",
    "\\beta, & \\text {if x=$\\alpha$} \\\\\n",
    "0, & \\text {else}\n",
    "\\end{cases}\n",
    "$$\n",
    "We need to ask for the value of the function at the x position, and at the same time we need to hide $\\alpha$ and $\\beta$. The scheme adopted is to have the DPF key over $\\alpha$ and $\\beta$ generated by a trusted third party, and then to distribute the key to the two parties, who each use the key to calculate the value of the function when the independent variable is x.\n",
    "\n",
    "Regarding the operation of shape transformation on x, we explain as follows: Since the supported representation data only supports 32 or 64 bits, to represent 128-bit data, you need the data of 4 int32s or 2 int64s to be concatenated together. This is the case for the random number ``s`` required by DPFKey. So we need to convert x to a column vector so that we don't have dimension mismatches when we perform subsequent multiplications."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-03-21T11:22:07.368352Z",
     "start_time": "2024-03-21T11:22:04.837407Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RingTensor\n",
      " value:tensor([0, 0, 0, 0, 1, 0, 0, 0, 0, 0]) \n",
      " dtype:int \n",
      " scale:1\n"
     ]
    }
   ],
   "source": [
    "# import the libraries\n",
    "from NssMPC.crypto.primitives import DPF\n",
    "from NssMPC.crypto.aux_parameter import DPFKey\n",
    "from NssMPC import RingTensor\n",
    "\n",
    "num_of_keys = 10  # We need a few keys for a few function values, but of course we can generate many keys in advance.\n",
    "\n",
    "# generate keys in offline phase\n",
    "# set alpha and beta\n",
    "alpha = RingTensor.convert_to_ring(5)\n",
    "beta = RingTensor.convert_to_ring(1)\n",
    "\n",
    "Key0, Key1 = DPFKey.gen(num_of_keys=num_of_keys, alpha=alpha, beta=beta)\n",
    "# online phase\n",
    "# generate some values what we need to evaluate\n",
    "x = RingTensor.convert_to_ring([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n",
    "shape=x.shape\n",
    "x = x.reshape(-1,1)\n",
    "\n",
    "# Party 0:\n",
    "res_0 = DPF.eval(x=x, keys=Key0, party_id=0)\n",
    "\n",
    "# Party 1:\n",
    "res_1 = DPF.eval(x=x, keys=Key1, party_id=1)\n",
    "\n",
    "# restore result\n",
    "res = res_0 + res_1\n",
    "res=res.reshape(shape)\n",
    "print(res)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DCF\n",
    "Definition：\n",
    "$$\n",
    "f_{\\alpha,\\beta}(x)=\\begin{cases}\n",
    "\\beta, & \\text {if x < $\\alpha$} \\\\\n",
    "0, & \\text {else}\n",
    "\\end{cases}\n",
    "$$\n",
    "The method for computing the DCF value is similar to that of DPF."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-03-21T11:22:07.928866Z",
     "start_time": "2024-03-21T11:22:07.370434Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RingTensor\n",
      " value:tensor([1, 1, 1, 1, 0, 0, 0, 0, 0, 0]) \n",
      " dtype:int \n",
      " scale:1\n"
     ]
    }
   ],
   "source": [
    "# import the libraries\n",
    "from NssMPC.crypto.primitives.function_secret_sharing.dcf import DCF\n",
    "from NssMPC.crypto.aux_parameter import DCFKey\n",
    "from NssMPC import RingTensor\n",
    "\n",
    "num_of_keys = 10  # We need a few keys for a few function values, but of course we can generate many keys in advance.\n",
    "\n",
    "# generate keys in offline phase\n",
    "# set alpha and beta\n",
    "alpha = RingTensor.convert_to_ring(5)\n",
    "beta = RingTensor.convert_to_ring(1)\n",
    "\n",
    "Key0, Key1 = DCF.gen(num_of_keys=num_of_keys, alpha=alpha, beta=beta)\n",
    "\n",
    "# online phase\n",
    "# generate some values what we need to evaluate\n",
    "x = RingTensor.convert_to_ring([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n",
    "\n",
    "# Party 0:\n",
    "res_0 = DCF.eval(x=x, keys=Key0, party_id=0)\n",
    "\n",
    "# Party 1:\n",
    "res_1 = DCF.eval(x=x, keys=Key1, party_id=1)\n",
    "\n",
    "# restore result\n",
    "res = res_0 + res_1\n",
    "print(res)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DICF\n",
    "definition：\n",
    "$$\n",
    "f_{p,q}(x)=\\begin{cases}\n",
    "1, & \\text {if p$\\leq$x $\\leq$ q} \\\\\n",
    "0, & \\text {else}\n",
    "\\end{cases}\n",
    "$$\n",
    "We set the function value to 1 when x falls within the range of p and q. With DICF, we can achieve a comparison between two numbers. For two numbers, x and y, if the difference between them falls within the positive range, it indicates that x is greater than y.\n",
    "Now we will demonstrate how to compute the value of the DICF for a given input x. However, unlike DPF and DCF, the DICF hides the information of the input x, rather than the information of the upper and lower bounds. This is done to serve the purpose of performing comparison."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The library supports three DICF operations: 2021 Function secret sharing, GROTTO, SIGMA."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-03-21T11:22:08.737868Z",
     "start_time": "2024-03-21T11:22:07.929985Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RingTensor\n",
      " value:tensor([0, 0, 1, 1, 1, 1, 1, 0, 0, 0]) \n",
      " dtype:int \n",
      " scale:1\n"
     ]
    }
   ],
   "source": [
    "# import the libraries\n",
    "from NssMPC.crypto.primitives.function_secret_sharing.dicf import DICF\n",
    "from NssMPC.crypto.aux_parameter import DICFKey\n",
    "from NssMPC import RingTensor\n",
    "\n",
    "# generate key in offline phase\n",
    "num_of_keys = 10\n",
    "down_bound = RingTensor(3)\n",
    "upper_bound = RingTensor(7)\n",
    "\n",
    "Key0, Key1 = DICFKey.gen(num_of_keys=num_of_keys, down_bound=down_bound, upper_bound=upper_bound)\n",
    "\n",
    "# evaluate x in online phase\n",
    "# generate some values what we need to evaluate\n",
    "x = RingTensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n",
    "x_shift = x + Key0.r_in.reshape(x.shape) + Key1.r_in.reshape(x.shape)\n",
    "\n",
    "# online phase\n",
    "# Party 0:\n",
    "res_0 = DICF.eval(x_shift=x_shift, keys=Key0, party_id=0, down_bound=down_bound, upper_bound=upper_bound)\n",
    "\n",
    "# Party 1:\n",
    "res_1 = DICF.eval(x_shift=x_shift, keys=Key1, party_id=1, down_bound=down_bound, upper_bound=upper_bound)\n",
    "\n",
    "# restore result\n",
    "res = res_0 + res_1\n",
    "print(res)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### GROTTO\n",
    "The paper GROTTO proposes a scheme to implement DICF faster with prefix sum and DPF. The following is the sample code. It should be noted that although the result evaluated by this scheme is 0,1, it actually corresponds to bool values False and True, that is to say, the result of this scheme is a Boolean secret share value, while the previous schemes are arithmetic secret share values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-03-21T11:22:09.126060Z",
     "start_time": "2024-03-21T11:22:08.739044Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RingTensor\n",
      " value:tensor([0, 0, 1, 1, 1, 1, 0, 0, 0, 0]) \n",
      " dtype:int \n",
      " scale:1\n"
     ]
    }
   ],
   "source": [
    "# import the libraries\n",
    "from NssMPC.crypto.primitives.function_secret_sharing.dicf import GrottoDICF\n",
    "from NssMPC.crypto.aux_parameter import GrottoDICFKey\n",
    "from NssMPC import RingTensor\n",
    "\n",
    "# generate key in offline phase\n",
    "num_of_keys = 10\n",
    "down_bound = RingTensor(3)\n",
    "upper_bound = RingTensor(7)\n",
    "beta = RingTensor(1)\n",
    "\n",
    "Key0, Key1 = GrottoDICFKey.gen(num_of_keys=num_of_keys, beta=beta)\n",
    "\n",
    "# evaluate x in online phase\n",
    "# generate some values what we need to evaluate\n",
    "x = RingTensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n",
    "x_shift = Key0.r_in.reshape(x.shape) + Key1.r_in.reshape(x.shape) - x\n",
    "\n",
    "# online phase\n",
    "# Party 0:\n",
    "res_0 = GrottoDICF.eval(x_shift=x_shift, key=Key0, party_id=0, down_bound=down_bound, upper_bound=upper_bound)\n",
    "\n",
    "# Party 1:\n",
    "res_1 = GrottoDICF.eval(x_shift=x_shift, key=Key1, party_id=1, down_bound=down_bound, upper_bound=upper_bound)\n",
    "\n",
    "# restore result\n",
    "res = res_0 ^ res_1\n",
    "print(res)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### SIGMA\n",
    "The paper SIGMA proposes an end-to-end system for secure transformer inference based on FSS, by constructing new FSS-based protocols for complex machine learning functionalities, such as Softmax, GeLU and SiLU, and also accelerating their computation on GPUs. It presents the first secure inference of generative pre-trained transformer (GPT) models. In particular, SIGMA executes Meta's LLaMA2 (available on HuggingFace) with 13 billion parameters in 44 seconds and GPT2 in 1.6 seconds."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RingTensor\n",
      " value:tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) \n",
      " dtype:int \n",
      " scale:1\n"
     ]
    }
   ],
   "source": [
    "# import the libraries\n",
    "from NssMPC.crypto.primitives.function_secret_sharing.dicf import SigmaDICF\n",
    "from NssMPC.crypto.aux_parameter import SigmaDICFKey\n",
    "from NssMPC import RingTensor\n",
    "\n",
    "# generate key in offline phase\n",
    "num_of_keys = 10\n",
    "\n",
    "Key0, Key1 = SigmaDICFKey.gen(num_of_keys=num_of_keys)\n",
    "\n",
    "# evaluate x in online phase\n",
    "# generate some values what we need to evaluate\n",
    "x = RingTensor([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])\n",
    "x_shift = Key0.r_in.reshape(x.shape) + Key1.r_in.reshape(x.shape) - x\n",
    "\n",
    "# online phase\n",
    "# Party 0:\n",
    "res_0 = SigmaDICF.eval(x_shift=x_shift, key=Key0, party_id=0)\n",
    "\n",
    "# Party 1:\n",
    "res_1 = SigmaDICF.eval(x_shift=x_shift, key=Key1, party_id=1)\n",
    "\n",
    "# restore result\n",
    "res = res_0 ^ res_1\n",
    "print(res)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The application of FSS\n",
    "As mentioned before, DICF can be used for number comparison. If you want to use DICF for number comparison, please change `GE_TYPE` in`./config/configs.py` to `DICF`, and then refer to the method in Tutorial_2 to achieve it. We also talked about an improved FSS method in Tutorial_0. If you want to use this method for number comparison, change `GE_TYPE` to `PPQ` or `SIGMA` and then refer to Tutorial_2."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.19"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
